package main

import (
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"jdfs2/internal/config"
	"jdfs2/internal/ginserver"
	"jdfs2/internal/storage"
)

const (
	Version = "1.0.0"
	Usage   = `JDFS2 - S3-compatible object storage server

Usage:
  jdfs2 <command> [options]

Commands:
  init      Initialize configuration file
  start     Start the server in background
  stop      Stop the server
  restart   Restart the server
  status    Show server status
  version   Show version information
  help      Show this help message

Examples:
  jdfs2 init                     # Initialize configuration file
  jdfs2 start                    # Start server with default config
  jdfs2 start -c /path/config    # Start with custom config
  jdfs2 stop                     # Stop server
  jdfs2 status                   # Check server status
`
)

var (
	configPath = flag.String("c", "", "Path to configuration file")
	port       = flag.Int("p", 0, "Override server port")
	dataRoot   = flag.String("d", "", "Override data root directory")
	verbose    = flag.Bool("verbose", false, "Verbose output")
)

func main() {
	if len(os.Args) < 2 {
		printUsage()
		os.Exit(1)
	}

	command := os.Args[1]
	os.Args = os.Args[1:] // Remove command from args for flag parsing

	switch command {
	case "init":
		initCommand()
	case "start":
		startCommand()
	case "stop":
		stopCommand()
	case "restart":
		restartCommand()
	case "status":
		statusCommand()
	case "version":
		versionCommand()
	case "server":
		serverMode()
	case "help", "-h", "--help":
		printUsage()
	default:
		fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command)
		printUsage()
		os.Exit(1)
	}
}

func printUsage() {
	fmt.Print(Usage)
}

func versionCommand() {
	fmt.Printf("JDFS2 version %s\n", Version)
}

func initCommand() {
	flag.Parse()

	// Load configuration with backup
	opt := config.Options{ConfigPath: *configPath}
	cfg, path, err := config.LoadOrCreateWithBackup(opt, true)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to initialize config %s: %v\n", path, err)
		os.Exit(1)
	}

	fmt.Printf("Configuration file initialized: %s\n", path)
	fmt.Printf("Server will listen on: %s:%d\n", string(cfg.Server.Listen), cfg.Server.Port)
	fmt.Printf("Data root: %s\n", cfg.GetDataRoot())
	fmt.Printf("Log directory: %s\n", cfg.GetLogDir())
}

func startCommand() {
	flag.Parse()

	// Load configuration
	opt := config.Options{ConfigPath: *configPath}
	cfg, path, err := config.LoadOrCreate(opt)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load config %s: %v\n", path, err)
		os.Exit(1)
	}

	// Override config with command line flags
	if *port > 0 {
		cfg.Server.Port = *port
	}
	if *dataRoot != "" {
		cfg.Server.DataRoot = config.Q(*dataRoot)
	}

	if *verbose {
		fmt.Printf("Using config: %s\n", path)
		fmt.Printf("Data root: %s\n", string(cfg.Server.DataRoot))
		fmt.Printf("Port: %d\n", cfg.Server.Port)
	}

	// Check if already running
	pidFile := cfg.GetPIDFile()
	if isRunning(pidFile) {
		pid := getPID(pidFile)
		fmt.Printf("Server already running with PID %d\n", pid)
		return
	}

	// Prepare environment
	_ = os.Setenv("S3_ROOT", cfg.GetDataRoot())
	_ = os.Setenv("S3_VOLUME_SIZE", fmt.Sprintf("%d", cfg.Storage.VolumeSize))

	// Create necessary directories
	createDirectories(cfg)

	// Clean up Badger locks
	cleanupBadgerLocks(cfg)

	// Start server
	addr := fmt.Sprintf("%s:%d", string(cfg.Server.Listen), cfg.Server.Port)
	logFile := cfg.GetLogFile()

	fmt.Printf("Starting JDFS2 server on %s...\n", addr)
	fmt.Printf("Logs: %s\n", logFile)

	// Start in background
	// Get absolute path to current executable
	execPath, err := os.Executable()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get executable path: %v\n", err)
		os.Exit(1)
	}
	cmd := exec.Command("nohup", execPath, "server")
	cmd.Env = os.Environ()

	// Redirect output to log file
	logFileHandle, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open log file: %v\n", err)
		os.Exit(1)
	}
	cmd.Stdout = logFileHandle
	cmd.Stderr = logFileHandle

	if err := cmd.Start(); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to start server: %v\n", err)
		os.Exit(1)
	}

	// Write PID to file
	if err := os.WriteFile(pidFile, []byte(fmt.Sprintf("%d", cmd.Process.Pid)), 0644); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write PID file: %v\n", err)
		os.Exit(1)
	}

	// Wait a moment and check if started successfully
	time.Sleep(1 * time.Second)
	if isRunning(pidFile) {
		pid := getPID(pidFile)
		fmt.Printf("Server started successfully with PID %d\n", pid)
	} else {
		fmt.Fprintf(os.Stderr, "Failed to start server, check logs: %s\n", logFile)
		os.Exit(1)
	}
}

func stopCommand() {
	flag.Parse()

	// Load configuration to get paths
	opt := config.Options{ConfigPath: *configPath}
	cfg, _, err := config.LoadOrCreate(opt)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load config: %v\n", err)
		os.Exit(1)
	}

	pidFile := cfg.GetPIDFile()
	port := cfg.Server.Port

	fmt.Println("Stopping JDFS2 server...")

	// Stop by PID file
	if isRunning(pidFile) {
		pid := getPID(pidFile)
		fmt.Printf("Stopping PID %d...\n", pid)
		if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
			fmt.Printf("Failed to kill PID %d: %v\n", pid, err)
		}
		os.Remove(pidFile)
	}

	// Stop by port
	stopByPort(port)

	// Clean up Badger locks
	cleanupBadgerLocks(cfg)

	fmt.Println("Server stopped. Badger locks cleaned.")
}

func restartCommand() {
	fmt.Println("Restarting JDFS2 server...")
	stopCommand()
	time.Sleep(2 * time.Second)
	startCommand()
}

func statusCommand() {
	flag.Parse()

	// Load configuration
	opt := config.Options{ConfigPath: *configPath}
	cfg, configPath, err := config.LoadOrCreate(opt)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load config: %v\n", err)
		os.Exit(1)
	}

	pidFile := cfg.GetPIDFile()
	if isRunning(pidFile) {
		pid := getPID(pidFile)
		fmt.Printf("=== JDFS2 Server Status ===\n")
		fmt.Printf("Status: Running\n")
		fmt.Printf("PID: %d\n", pid)
		fmt.Printf("Port: %d\n", cfg.Server.Port)
		fmt.Printf("Listen: %s\n", string(cfg.Server.Listen))
		fmt.Printf("Data root: %s\n", cfg.GetDataRoot())
		fmt.Printf("Log directory: %s\n", cfg.GetLogDir())
		fmt.Printf("Config file: %s\n", configPath)

		// 显示配置文件内容
		fmt.Printf("\n=== Configuration ===\n")
		configContent, err := os.ReadFile(configPath)
		if err == nil {
			lines := strings.Split(string(configContent), "\n")
			for _, line := range lines {
				if strings.TrimSpace(line) != "" && !strings.HasPrefix(line, "#") {
					fmt.Printf("%s\n", line)
				}
			}
		}

		// 显示存储统计信息
		fmt.Printf("\n=== Storage Statistics ===\n")
		showStorageStats(cfg)

	} else {
		fmt.Printf("=== JDFS2 Server Status ===\n")
		fmt.Printf("Status: Not running\n")
		fmt.Printf("Config file: %s\n", configPath)
		fmt.Printf("Data root: %s\n", cfg.GetDataRoot())

		// 显示配置文件内容
		fmt.Printf("\n=== Configuration ===\n")
		configContent, err := os.ReadFile(configPath)
		if err == nil {
			lines := strings.Split(string(configContent), "\n")
			for _, line := range lines {
				if strings.TrimSpace(line) != "" && !strings.HasPrefix(line, "#") {
					fmt.Printf("%s\n", line)
				}
			}
		}

		// 显示存储统计信息（服务器停止时可以访问数据库）
		fmt.Printf("\n=== Storage Statistics ===\n")
		showStorageStatsWhenStopped(cfg)
	}
}

// Server mode (called by start command)
func serverMode() {
	opt := config.Options{ConfigPath: *configPath}
	cfg, _, err := config.LoadOrCreate(opt)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err)
		os.Exit(1)
	}

	_ = os.Setenv("S3_ROOT", cfg.GetDataRoot())
	_ = os.Setenv("S3_VOLUME_SIZE", fmt.Sprintf("%d", cfg.Storage.VolumeSize))
	addr := fmt.Sprintf("%s:%d", string(cfg.Server.Listen), cfg.Server.Port)
	if err := ginserver.RunWithAddr(addr); err != nil {
		fmt.Fprintf(os.Stderr, "server error: %v\n", err)
		os.Exit(1)
	}
}

// Helper functions

func isRunning(pidFile string) bool {
	if _, err := os.Stat(pidFile); os.IsNotExist(err) {
		return false
	}

	pid := getPID(pidFile)
	if pid <= 0 {
		return false
	}

	// Check if process exists
	if err := syscall.Kill(pid, 0); err != nil {
		os.Remove(pidFile) // Clean up stale PID file
		return false
	}

	return true
}

func getPID(pidFile string) int {
	data, err := os.ReadFile(pidFile)
	if err != nil {
		return 0
	}

	pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
	if err != nil {
		return 0
	}

	return pid
}

// showStorageStats 显示存储统计信息
func showStorageStats(cfg config.Config) {
	// 由于服务器正在运行，Badger数据库被锁定，我们只能通过文件系统来获取基本信息
	dataRoot := cfg.GetDataRoot()

	// 读取数据根目录，获取bucket信息
	entries, err := os.ReadDir(dataRoot)
	if err != nil {
		fmt.Printf("Failed to read data root: %v\n", err)
		return
	}

	var buckets []os.DirEntry
	for _, entry := range entries {
		if entry.IsDir() && entry.Name() != ".meta" {
			buckets = append(buckets, entry)
		}
	}

	fmt.Printf("Total buckets: %d\n", len(buckets))

	if len(buckets) == 0 {
		fmt.Printf("No buckets found\n")
		return
	}

	totalObjects := 0
	totalDeletedObjects := 0
	totalSize := int64(0)

	for _, bucket := range buckets {
		bucketName := bucket.Name()
		bucketPath := filepath.Join(dataRoot, bucketName)

		// 获取bucket创建时间
		info, err := bucket.Info()
		if err != nil {
			continue
		}

		fmt.Printf("\n--- Bucket: %s ---\n", bucketName)
		fmt.Printf("Created: %s\n", info.ModTime().Format("2006-01-02 15:04:05"))

		// 统计bucket中的对象
		activeObjectCount := 0
		bucketSize := int64(0)

		err = filepath.WalkDir(bucketPath, func(path string, d os.DirEntry, err error) error {
			if err != nil {
				return nil // 忽略错误，继续统计
			}
			if d.IsDir() {
				return nil
			}

			// 跳过隐藏文件和元数据文件
			name := d.Name()
			if strings.HasPrefix(name, ".") {
				return nil
			}

			// 检查是否是已删除的对象（通过文件名模式判断）
			// 已删除的对象通常会有特殊的命名模式，或者我们可以通过其他方式判断
			// 这里我们先简单统计所有可见文件为active对象
			activeObjectCount++

			// 获取文件大小
			if info, err := d.Info(); err == nil {
				bucketSize += info.Size()
			}

			return nil
		})

		if err != nil {
			fmt.Printf("Failed to scan bucket: %v\n", err)
			continue
		}

		totalObjects += activeObjectCount
		totalSize += bucketSize

		fmt.Printf("Active objects: %d\n", activeObjectCount)
		fmt.Printf("Total size: %s\n", formatBytes(bucketSize))

		// 尝试通过多种方式获取已删除对象数量
		deletedCount := 0
		deletedSource := ""

		// 方法1：尝试通过HTTP API获取
		if apiCount := getDeletedObjectsCountViaAPI(cfg.Server.Port, bucketName); apiCount > 0 {
			deletedCount = apiCount
			deletedSource = "API"
		} else {
			// 方法2：通过文件系统估算
			deletedCount = estimateDeletedObjectsFromFilesystem(bucketPath)
			if deletedCount > 0 {
				deletedSource = "filesystem"
			} else {
				deletedSource = "unavailable (database locked)"
			}
		}

		totalDeletedObjects += deletedCount

		if deletedCount > 0 {
			fmt.Printf("Deleted objects: %d (from %s)\n", deletedCount, deletedSource)
		} else {
			fmt.Printf("Deleted objects: 0 (%s)\n", deletedSource)
		}
	}

	// 显示总体统计
	fmt.Printf("\n=== Overall Statistics ===\n")
	fmt.Printf("Total buckets: %d\n", len(buckets))
	fmt.Printf("Total active objects: %d\n", totalObjects)
	fmt.Printf("Total deleted objects: %d (estimated)\n", totalDeletedObjects)
	fmt.Printf("Total storage used: %s\n", formatBytes(totalSize))
	fmt.Printf("Volume size: %s\n", formatBytes(cfg.Storage.VolumeSize))

	// 显示卷信息
	fmt.Printf("\n=== Volume Information ===\n")
	showVolumeInfo(cfg)
}

// showVolumeInfo 显示卷信息
func showVolumeInfo(cfg config.Config) {
	dataRoot := cfg.GetDataRoot()
	volumesDir := filepath.Join(dataRoot, "volumes")

	entries, err := os.ReadDir(volumesDir)
	if err != nil {
		fmt.Printf("No volumes directory found\n")
		return
	}

	var volumes []os.DirEntry
	for _, entry := range entries {
		if !entry.IsDir() && strings.HasPrefix(entry.Name(), "volume_") {
			volumes = append(volumes, entry)
		}
	}

	fmt.Printf("Total volumes: %d\n", len(volumes))

	if len(volumes) == 0 {
		fmt.Printf("No volumes found\n")
		return
	}

	totalVolumeSize := int64(0)
	for _, volume := range volumes {
		if info, err := volume.Info(); err == nil {
			totalVolumeSize += info.Size()
		}
	}

	fmt.Printf("Total volume size: %s\n", formatBytes(totalVolumeSize))
	fmt.Printf("Configured volume size: %s\n", formatBytes(cfg.Storage.VolumeSize))
}

// getDeletedObjectsCountViaAPI 通过HTTP API获取删除对象数量
func getDeletedObjectsCountViaAPI(port int, bucketName string) int {
	// 尝试通过HTTP API获取删除对象信息
	// 这里我们暂时返回0，因为当前JDFS2可能没有暴露删除对象统计的API
	// 未来可以添加一个专门的API端点来获取这些信息

	client := &http.Client{Timeout: 5 * time.Second}
	url := fmt.Sprintf("http://localhost:%d/admin/stats/deleted/%s", port, bucketName)

	resp, err := client.Get(url)
	if err != nil {
		return 0 // API不可用，返回0
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return 0
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return 0
	}

	// 解析响应，假设返回的是JSON格式：{"count": 5}
	// 这里简化处理，实际应该解析JSON
	countStr := strings.TrimSpace(string(body))
	if count, err := strconv.Atoi(countStr); err == nil {
		return count
	}

	return 0
}

// estimateDeletedObjectsFromFilesystem 通过文件系统估算已删除对象数量
func estimateDeletedObjectsFromFilesystem(bucketPath string) int {
	// 这里我们可以通过一些启发式方法来估算已删除对象
	// 例如：检查是否有临时文件、部分文件、或者其他指标

	// 方法1：检查是否有以特定前缀命名的文件（表示已删除）
	deletedCount := 0

	err := filepath.WalkDir(bucketPath, func(path string, d os.DirEntry, err error) error {
		if err != nil {
			return nil
		}
		if d.IsDir() {
			return nil
		}

		name := d.Name()

		// 检查是否有表示已删除的文件模式
		// 例如：以 .deleted 结尾的文件，或者以 _deleted_ 命名的文件
		if strings.HasSuffix(name, ".deleted") ||
			strings.Contains(name, "_deleted_") ||
			strings.HasPrefix(name, ".deleted_") {
			deletedCount++
		}

		return nil
	})

	if err != nil {
		return 0
	}

	// 方法2：检查是否有部分文件（可能是删除过程中的残留）
	// 这里我们暂时返回0，因为JDFS2的删除机制可能不会留下明显的文件系统痕迹
	// 真正的删除统计需要访问Badger数据库

	return deletedCount
}

// showStorageStatsWhenStopped 当服务器停止时显示详细的存储统计信息
func showStorageStatsWhenStopped(cfg config.Config) {
	// 由于服务器停止，可以访问Badger数据库
	metaPath := filepath.Join(cfg.GetDataRoot(), ".meta", "badger")
	metaStore, err := storage.NewDistributedMetaStore(metaPath, "stats-node")
	if err != nil {
		fmt.Printf("Failed to initialize meta store: %v\n", err)
		// 回退到文件系统统计
		showStorageStats(cfg)
		return
	}
	defer metaStore.Close()

	dataRoot := cfg.GetDataRoot()

	// 读取数据根目录，获取bucket信息
	entries, err := os.ReadDir(dataRoot)
	if err != nil {
		fmt.Printf("Failed to read data root: %v\n", err)
		return
	}

	var buckets []os.DirEntry
	for _, entry := range entries {
		if entry.IsDir() && entry.Name() != ".meta" {
			buckets = append(buckets, entry)
		}
	}

	fmt.Printf("Total buckets: %d\n", len(buckets))

	if len(buckets) == 0 {
		fmt.Printf("No buckets found\n")
		return
	}

	totalObjects := 0
	totalDeletedObjects := 0
	totalSize := int64(0)

	for _, bucket := range buckets {
		bucketName := bucket.Name()
		bucketPath := filepath.Join(dataRoot, bucketName)

		// 获取bucket创建时间
		info, err := bucket.Info()
		if err != nil {
			continue
		}

		fmt.Printf("\n--- Bucket: %s ---\n", bucketName)
		fmt.Printf("Created: %s\n", info.ModTime().Format("2006-01-02 15:04:05"))

		// 统计bucket中的对象
		objectCount := 0
		bucketSize := int64(0)

		err = filepath.WalkDir(bucketPath, func(path string, d os.DirEntry, err error) error {
			if err != nil {
				return nil // 忽略错误，继续统计
			}
			if d.IsDir() {
				return nil
			}

			// 跳过隐藏文件和元数据文件
			name := d.Name()
			if strings.HasPrefix(name, ".") {
				return nil
			}

			objectCount++

			// 获取文件大小
			if info, err := d.Info(); err == nil {
				bucketSize += info.Size()
			}

			return nil
		})

		if err != nil {
			fmt.Printf("Failed to scan bucket: %v\n", err)
			continue
		}

		totalObjects += objectCount
		totalSize += bucketSize

		fmt.Printf("Active objects: %d\n", objectCount)
		fmt.Printf("Total size: %s\n", formatBytes(bucketSize))

		// 获取已删除的对象记录
		deletedRecords, err := metaStore.ListDeletedRecordsByBucket(bucketName)
		if err != nil {
			fmt.Printf("Failed to list deleted records: %v\n", err)
		} else {
			deletedCount := len(deletedRecords)
			totalDeletedObjects += deletedCount
			fmt.Printf("Deleted objects: %d\n", deletedCount)

			// 显示最近删除的对象
			if deletedCount > 0 {
				fmt.Printf("Recently deleted:\n")
				for i, record := range deletedRecords {
					if i >= 5 { // 只显示最近5个
						fmt.Printf("  ... and %d more\n", deletedCount-5)
						break
					}
					fmt.Printf("  %s (deleted at %s)\n",
						record.Key,
						record.DeletedAt.Format("2006-01-02 15:04:05"))
				}
			}
		}
	}

	// 显示总体统计
	fmt.Printf("\n=== Overall Statistics ===\n")
	fmt.Printf("Total buckets: %d\n", len(buckets))
	fmt.Printf("Total active objects: %d\n", totalObjects)
	fmt.Printf("Total deleted objects: %d\n", totalDeletedObjects)
	fmt.Printf("Total storage used: %s\n", formatBytes(totalSize))
	fmt.Printf("Volume size: %s\n", formatBytes(cfg.Storage.VolumeSize))

	// 显示卷信息
	fmt.Printf("\n=== Volume Information ===\n")
	showVolumeInfo(cfg)
}

// formatBytes 格式化字节数为可读格式
func formatBytes(bytes int64) string {
	const unit = 1024
	if bytes < unit {
		return fmt.Sprintf("%d B", bytes)
	}
	div, exp := int64(unit), 0
	for n := bytes / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}

func createDirectories(cfg config.Config) {
	dirs := []string{
		filepath.Dir(cfg.GetLogFile()),
		filepath.Dir(cfg.GetPIDFile()),
		cfg.GetDataRoot(),
		filepath.Join(cfg.GetDataRoot(), ".meta", "badger"),
	}

	for _, dir := range dirs {
		os.MkdirAll(dir, 0755)
	}
}

func cleanupBadgerLocks(cfg config.Config) {
	badgerDir := filepath.Join(cfg.GetDataRoot(), ".meta", "badger")
	lockFiles := []string{
		filepath.Join(badgerDir, "LOCK"),
		filepath.Join(badgerDir, "LOCK*"),
	}

	for _, lockFile := range lockFiles {
		os.Remove(lockFile)
	}
}

func stopByPort(port int) {
	// This is a simplified version - in production you might want to use
	// more sophisticated port detection
	fmt.Printf("Checking for processes on port %d...\n", port)
	// Implementation would depend on system tools available
}

func execCommand(cmd string) error {
	parts := strings.Fields(cmd)
	if len(parts) == 0 {
		return fmt.Errorf("empty command")
	}

	command := exec.Command(parts[0], parts[1:]...)
	command.Stdout = os.Stdout
	command.Stderr = os.Stderr

	return command.Run()
}
